package org.zjvis.datascience.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.FileSystemResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;
import org.zjvis.datascience.common.dto.*;
import org.zjvis.datascience.common.dto.KeyValueDTO;
import org.zjvis.datascience.common.dto.ModelLocalDTO;
import org.zjvis.datascience.common.dto.TaskDTO;
import org.zjvis.datascience.common.enums.CommonTypeEnum;
import org.zjvis.datascience.common.enums.FieldTypeEnum;
import org.zjvis.datascience.common.enums.InstallStatusEnum;
import org.zjvis.datascience.common.enums.ModelFieldEnum;
import org.zjvis.datascience.common.enums.TaskTypeEnum;
import org.zjvis.datascience.common.model.ApiResult;
import org.zjvis.datascience.common.model.ApiResultCode;
import org.zjvis.datascience.common.util.DozerUtil;
import org.zjvis.datascience.common.util.JwtUtil;
import org.zjvis.datascience.common.util.KeyValueUtil;
import org.zjvis.datascience.common.util.ModelUtil;
import org.zjvis.datascience.common.util.RestTemplateUtil;
import org.zjvis.datascience.common.vo.TaskVO;
import org.zjvis.datascience.service.dag.TaskRunnerResult;
import org.zjvis.datascience.service.mapper.*;

import javax.servlet.http.HttpServletResponse;

@Service
public class ModelLocalService {

    protected final static Logger logger = LoggerFactory.getLogger("ModelLocalService");

    @Autowired
    private ModelLocalMapper modelLocalMapper;

    @Autowired
    private TaskMapper taskMapper;

    @Autowired
    private TaskInstanceMapper taskInstanceMapper;

    @Autowired
    private RestTemplateUtil restTemplateUtil;

    @Autowired
    private FileService fileService;

    @Autowired
    private ProjectMapper projectMapper;

    @Lazy
    @Autowired
    private TaskService taskService;

    @Autowired
    private PipelineMapper pipelineMapper;

    @Value("${upload.model.folder-path}")
    private String folderPath;

    @Value("${postgres.host}")
    private String gpHost;

    @Value("${postgres.username}")
    private String gpUser;

    @Value("${postgres.database}")
    private String gpDbname;

    @Value("${postgres.port}")
    private String gpPort;

    @Value("${postgres.password}")
    private String gpPassword;

    @Value("${spring.datasource.druid.host}")
    private String mysqlHost;

    @Value("${spring.datasource.druid.username}")
    private String mysqlUser;

    private String mysqlDb = "aiworks";

    @Value("${spring.datasource.druid.port}")
    private String mysqlPort;

    @Value("${restful.insightServer.frontend}")
    private String insightFrontend;

    @Value("${spring.datasource.druid.password}")
    private String mysqlPassword;


    private String KEY_MAIN = "main";

    private String KEY_OUTPUTDF = "output_df";

    private static String loadDataTpl = "PGPASSWORD=%s psql -h %s -U %s -d %s -p %s -c \"\\copy (select * from %s order by _record_id_) TO '%s' CSV HEADER\"";

    private static String saveDbTpl = "python3 %s --file_path %s --table_name %s --task_instance_id %d --mysql_db %s --mysql_user %s --mysql_password %s --mysql_host %s --mysql_port %s --gp_db %s --gp_user %s --gp_password %s --gp_host %s --gp_port %s";

    private static String saveFileToDBTpl = "python3 %s --file_path %s --table_name %s  --gp_db %s --gp_user %s --gp_password %s --gp_host %s --gp_port %s";

    public JSONObject loadPanel(JSONObject params) {
        JSONObject panels = new JSONObject(new LinkedHashMap());

        JSONObject fieldPanels = new JSONObject(new LinkedHashMap());
        for (FieldTypeEnum type : FieldTypeEnum.values()) {
            String name = type.getDesc();
            List<KeyValueDTO> names = new ArrayList<>();
            params.put("field", name);
            names = modelLocalMapper.queryByField(params);
            if (names.size() > 0) {
                fieldPanels.put(name, KeyValueUtil.toInt(names, "name", "type"));
            }
        }
        panels.put(ModelFieldEnum.FIELD.getDesc(), fieldPanels);

        JSONObject commonPanels = new JSONObject(new LinkedHashMap());
        for (CommonTypeEnum type : CommonTypeEnum.values()) {
            String name = type.getDesc();
            List<KeyValueDTO> names = new ArrayList<>();
            params.put("field", name);
            names = modelLocalMapper.queryByField(params);
            if (names.size() > 0) {
                commonPanels.put(name, KeyValueUtil.toInt(names, "name", "type"));
            }
        }
        panels.put(ModelFieldEnum.COMMON.getDesc(), commonPanels);

        return panels;
    }

    public void loadModel(Long userId, Long projectId, Long folderId, JSONArray modelIds) {
        JSONObject params = new JSONObject();
        params.put("userId", userId);
        for (Object obj : modelIds) {
            Long modelId = new Long(obj.toString());
            params.put("modelId", modelId);
            ModelLocalDTO model = modelLocalMapper.queryByModelId(params).get(0);
            model.setProjectId(projectId);
            if (folderId != null) {
                model.setFolderId(folderId);
            }
            model.setUserId(userId);
            save(model);
        }
    }

    public int fieldToInt(String field) {
        String[] fields = field.split(",");
        for (String f : fields) {
            for (CommonTypeEnum type : CommonTypeEnum.values()) {
                if (f.equals(type.getDesc())) {
                    return type.getVal();
                }
            }
            for (FieldTypeEnum type : FieldTypeEnum.values()) {
                if (f.equals(type.getDesc())) {
                    return type.getVal();
                }
            }
        }
        return -1;
    }

    public Long save(ModelLocalDTO model) {
        modelLocalMapper.save(model);
        return model.getId();
    }

    public Long create(ModelLocalDTO model) {
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("yyyyMMdd");
        String now = format.format(date);
        model.setVersion(String.format("%s_1", now));
        Long modelId = modelLocalMapper.queryLatestModelId();
        model.setModelId(modelId + 1);
        return save(model);
    }

    public void update(ModelLocalDTO model) {
        modelLocalMapper.update(model);
    }

    public void updateAll() {
        List<ModelLocalDTO> models = modelLocalMapper.queryAll();
        JSONObject params = new JSONObject();
        for (ModelLocalDTO model : models) {
            Long id = model.getModelId();
            params.put("id", id);
            Object ret = restTemplateUtil.submitMOJob(params, "queryById").getResult();
            JSONObject modelOnline = JSONObject.parseObject(JSONObject.toJSONString(ret));
            model.setStar(modelOnline.getLong("star"));
            model.setWatch(modelOnline.getLong("watch"));
            model.setDownload(modelOnline.getLong("download"));
            model.setProfile(modelOnline.getString("profile"));
            model.setOwner(modelOnline.getString("owner"));
            model.setField(modelOnline.getString("field"));
            model.setIcon(modelOnline.getString("icon"));
            model.setCover(modelOnline.getString("cover"));
            LocalDateTime publicTime =
                    LocalDateTime.parse(modelOnline.getString("publicTime"),
                            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            model.setPublicTime(publicTime);
            modelLocalMapper.update(model);
        }
    }

    public JSONObject query(JSONObject params) {
        JSONObject newParams = ModelUtil.prepareQuery(params);
        List<ModelLocalDTO> models = modelLocalMapper.complexQuery(newParams);
        JSONObject ret = new JSONObject();
        ret.put("data", models);

        Long totalElements = queryRecords(newParams);
        ret.put("totalElements", totalElements);

        Long totalPages = 0L;
        Long pageIndex = params.getLong("pageIndex");
        Long pageSize = params.getLong("pageSize");
        if (totalElements % pageSize == 0) {
            totalPages = totalElements / pageSize;
        } else {
            totalPages = totalElements / pageSize + 1;
        }
        ret.put("totalPages", totalPages);
        ret.put("pageSize", pageSize);
        ret.put("pageIndex", pageIndex);
        return ret;
    }

    public JSONObject queryOnline(JSONObject params) {
        params.put("sendIndex", 0);
        Object ret = restTemplateUtil.submitMOJob(params, "query").getResult();
        JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(ret));
        int pageSize = jsonObject.getInteger("pageSize");
        int totalElements = jsonObject.getInteger("totalElements");
        int totalPages = jsonObject.getInteger("totalPages");
        int pageIndex = params.getInteger("pageIndex");
        int sendSize = 20;
        int sendNum;
        JSONArray modelOnlines = jsonObject.getJSONArray("data");
        if (pageIndex < totalPages) {
            sendNum = (int) Math.ceil(1.0 * pageSize / sendSize);
        } else {
            sendNum = (int) Math.ceil(1.0 * (totalElements - (totalPages - 1) * pageSize) / sendSize);
        }
        for (int i = 1; i < sendNum; i++) {
            params.put("sendIndex", i);
            Object retTmp = restTemplateUtil.submitMOJob(params, "query").getResult();
            JSONObject jsonObjectTmp = JSONObject.parseObject(JSONObject.toJSONString(retTmp));
            JSONArray batchModels = jsonObjectTmp.getJSONArray("data");
            modelOnlines.addAll(batchModels);
        }

        List<Long> ids = new ArrayList<>();
        for (int i = 0; i < modelOnlines.size(); i++) {
            JSONObject modelOnline = modelOnlines.getJSONObject(i);
            modelOnline.put("install", false);
            modelOnline.put("load", false);
            ids.add(modelOnline.getLong("id"));
        }

        List<Long> modelIds = modelLocalMapper.queryModelIds(params.getLong("userId"));
        for (Long modelId : modelIds) {
            int index = ids.indexOf(modelId);
            if (index > -1) {
                modelOnlines.getJSONObject(index).put("install", true);

                if (params.getString("projectId") != null) {
                    params.put("modelId", modelId);
                    List<ModelLocalDTO> tmp = modelLocalMapper.queryByModelIdAndProjectId(params);
                    if (tmp.size() > 0) {
                        modelOnlines.getJSONObject(index).put("load", true);
                    }
                }
            }
        }
        if (params.getBoolean("sort") != null && params.getBoolean("sort")) {
            List<JSONObject> modelList = modelOnlines.toJavaList(JSONObject.class);
            for (int i = 0; i < modelOnlines.size(); i++) {
                JSONObject modelOnline = modelOnlines.getJSONObject(i);
                if (modelOnline.getBoolean("install") && !modelOnline.getBoolean("load")) {
                    modelList.remove(i);
                    modelList.add(0, modelOnline);
                }
            }
            modelOnlines = JSONArray.parseArray(JSONArray.toJSONString(modelList));
            jsonObject.put("data", modelOnlines);
        }
        return jsonObject;
    }

    public JSONObject queryByIdOnline(JSONObject params) {
        Object ret = restTemplateUtil.submitMOJob(params, "queryById").getResult();
        JSONObject model = JSONObject.parseObject(JSONObject.toJSONString(ret));
        params.put("modelId", params.getLong("id"));
        List<ModelLocalDTO> modelLocals = modelLocalMapper.queryByModelId(params);
        if (modelLocals.size() > 0) {
            model.put("install", true);
        } else {
            model.put("install", false);
        }
        return model;
    }

    public Long queryRecords(JSONObject params) {
        return modelLocalMapper.queryRecords(params);
    }

    public ModelLocalDTO queryById(Long id) {
        return modelLocalMapper.queryById(id);
    }

    public List<ModelLocalDTO> queryByName(String name) {
        return modelLocalMapper.queryByName(name);
    }

    public List<ProjectDTO> queryProjectIds(JSONObject params) {
        List<Long> projectIds = modelLocalMapper.queryProjectIds(params);
        Set<Long> ids = new HashSet<>(projectIds);
        Set<Long> usedIds = new HashSet<>();
        for (Long projectId: ids) {
            List<PipelineDTO> pipelines = pipelineMapper.queryByProject(projectId);
            for (PipelineDTO pipeline: pipelines) {
                Long modelId = params.getLong("modelId");
                Long authRes = modelDelAuth(modelId, pipeline.getId(), true);
                if (authRes != 0L) {
                    usedIds.add(projectId);
                    break;
                }
            }
        }
        if (usedIds.size() != 0) {
            return projectMapper.queryByIds(usedIds);
        }
        return new ArrayList<>();
    }

    public List<ModelLocalDTO> queryByUid(JSONObject params) {
        return modelLocalMapper.queryByUid(params);
    }

    public void delete(Long id) {
        ModelLocalDTO model = modelLocalMapper.queryById(id);
        Long modelId = model.getModelId();
        Long uid = model.getUid();
        Long userId = model.getUserId();

        long currentUserId = JwtUtil.getCurrentUserId();
        //删除用户已安装和加载的算子

        JSONObject params = new JSONObject();
        params.put("modelId", modelId);
        params.put("userId", currentUserId);
        modelLocalMapper.deleteByModelId(params);

        params.put("userId", userId);
        params.put("uid", uid);
        List<ModelLocalDTO> models = modelLocalMapper.queryByUid(params);
        int numVersion = models.size();
        for (int i = 0; i < numVersion; i++) {
            ModelLocalDTO modelTmp = models.get(i);
            modelTmp.setNumVersion(numVersion);
            if (i == 0) {
                modelTmp.setTopVersion(true);
            }
            modelLocalMapper.update(modelTmp);
        }
    }

    public void deleteFromProject(Long id) {
        modelLocalMapper.delete(id);
    }

    public Long modelDelAuth(Long id, Long pipelineId, boolean useModelId) {
        if (null != pipelineId) {
            List<TaskDTO> tasks = taskMapper.queryByPipeline(pipelineId);
            for (TaskDTO task : tasks) {
                String data = task.getDataJson();
                JSONObject dataJson = JSONObject.parseObject(data);
                if (task.getType() == TaskTypeEnum.TASK_TYPE_AM.getVal() && dataJson
                        .containsKey("modelId")) {
                    Long modelIdInPipeline = dataJson.getLong("modelId");
                    if (useModelId) {
                        ModelLocalDTO modelLocalDTO = modelLocalMapper.queryById(modelIdInPipeline);
                        if (modelLocalDTO != null) {
                            modelIdInPipeline = modelLocalDTO.getModelId();
                        }
                    }
                    if (id.equals(modelIdInPipeline)) {
                        return id;
                    }
                }
            }
        }
        return 0L;
    }

    public ApiResult<Long> install(JSONObject params) {
        Long id = params.getLong("id");
        Long userId = JwtUtil.getCurrentUserId();
        params.put("userId", userId);
        params.put("modelId", id);
        List<ModelLocalDTO> modelLocals = modelLocalMapper.queryByModelId(params);
        if (modelLocals.size() > 0) {
            return ApiResult.valueOf(ApiResultCode.DUP_DOWNLOAD);
        }

        String installRes = restTemplateUtil.installFromInsight(id);
        if (!installRes.equals("success")) {
            return ApiResult.valueOf(ApiResultCode.DOWNLOAD);
        }
        Object ret = restTemplateUtil.submitMOJob(params, "install").getResult();
        JSONObject modelOnline = JSONObject.parseObject(JSONObject.toJSONString(ret));
        modelOnline.remove("gmtCreate");
        modelOnline.remove("gmtModify");
        LocalDateTime publicTime =
                LocalDateTime.parse(modelOnline.getString("publicTime"),
                        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        modelOnline.put("publicTime", publicTime);
        Long modelId = modelOnline.getLong("id");
        ModelLocalDTO model = DozerUtil.mapper(modelOnline, ModelLocalDTO.class);
        model.setModelId(modelId);
        model.setTopVersion(true);
        model.setInstallStatus(InstallStatusEnum.INSTALLED.getVal());
        model.setUserId(userId);
        model.setGmtCreator(userId);
        model.setGmtModifier(userId);
        Long newId = save(model);

        List<ModelLocalDTO> models = modelLocalMapper.queryByUid(params);
        int numVersion = models.size();
        for (int i = 0; i < numVersion; i++) {
            ModelLocalDTO modelTmp = models.get(i);
            modelTmp.setNumVersion(numVersion);
            if (i == 1) {
                modelTmp.setTopVersion(false);
            }
            modelLocalMapper.update(modelTmp);
        }
        return ApiResult.valueOf(newId);
    }

    public String getDirPathByMOId(Long moId) {
        return folderPath + "mo_" + moId;
    }

    /**
     * copy & rename directory of the model file
     *
     * @param task
     */
    public void load(TaskVO task) {
        Long modelLocalId = task.getData().getLong("modelId");
        Long modelOnlineId = this.queryById(modelLocalId).getModelId();
        File moFileToMove = new File(this.getDirPathByMOId(modelOnlineId));
        String moDirPath = this.getDirPathByMOId(modelOnlineId);
//        if (!moFileToMove.exists()){
//            return;
//        }
        String TARGET_FILE = folderPath + "ml_" + task.getId();
        //move
        JSONObject params = new JSONObject();
        params.put("apiPath", "loadToPipeline");
        params.put("moDirPath", moDirPath);
        params.put("target", TARGET_FILE);
        try {
            restTemplateUtil.submitFlaskJob(params);
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("error in am-load:" + e);
        }

//        //old version
//        try{
//            moFileToMove.renameTo(new File(TARGET_FILE));
//        }catch (Exception e){
//            logger.error("Move File Exception:" + e);
//        }
//        try {
//            fileService.unzip(folderPath + "mo_" + modelOnlineId + ".zip", new File(folderPath));
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
    }

    public void copyDir(Long oldTaskId, Long newTaskId) {
        JSONObject params = new JSONObject();
        params.put("apiPath", "copyDir");
        params.put("source", folderPath + "ml_" + oldTaskId);
        params.put("target", folderPath + "ml_" + newTaskId);
        try {
            restTemplateUtil.submitFlaskJob(params);
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("error in copyDir:" + e);
        }
    }

    public JSONObject queryCase(JSONObject params) {
        Object newCaseObject = restTemplateUtil.submitMOJob(params, "queryCase").getResult();
        JSONObject newCase = JSONObject.parseObject(JSONObject.toJSONString(newCaseObject));
        List<JSONObject> caseOnline = (List<JSONObject>) newCase.get("data");
        for (int i = 0; i < caseOnline.size(); i++) {
            JSONObject oneCase = caseOnline.get(i);
            String url = oneCase.getString("caseUrl");
            String img = oneCase.getString("caseImg");
            String newUrl = insightFrontend + url ;
            String newImgUrl = insightFrontend +img ;
            oneCase.put("caseUrl",newUrl);
            oneCase.put("caseImg",newImgUrl);
        }
        return newCase;
    }

    public JSONObject getFormData(TaskDTO task) {
        JSONObject cData = JSONObject.parseObject(task.getDataJson());
        JSONArray setParamsJsonArray = cData.getJSONArray("setParams");
        JSONObject setParams = setParamsJsonArray.getJSONObject(0);
        return setParams.getJSONObject("formData");
    }


    public JSONObject newCaseAndMost(long userId) {


        JSONObject res = new JSONObject();
        Object newcase = restTemplateUtil.submitMOJob(res, "newCaseAndMost").getResult();
        JSONObject newcase1 = JSONObject.parseObject(JSONObject.toJSONString(newcase));
        List<JSONObject> model = (List<JSONObject>) newcase1.get("data");
        List<ModelLocalDTO> localmodel = modelLocalMapper.queryUserLocal(userId);

        Set set = new HashSet<>();
        for (int i = 0; i < localmodel.size(); i++) {
            set.add(localmodel.get(i).getUid());
        }
        for (int i = 0; i < model.size(); i++) {
            JSONObject modelOnline = model.get(i);
            Long uid = modelOnline.getLong("uid");
            if (set.contains(uid)) {
                modelOnline.put("install", true);
            } else {
                modelOnline.put("install", false);
            }
        }


        res.put("data", model);


        return res;
    }

    public TaskRunnerResult execRunner(TaskInstanceDTO instance) {
        Long taskId = instance.getTaskId();
        TaskDTO childNode = taskService.queryById(taskId);
        TaskDTO parentNode = taskService.queryById(Long.valueOf(childNode.getParentId()));
        String gpDataName = taskService.getOutputTableName(parentNode);
        loadData(gpDataName, childNode.getId(), parentNode.getId());

        TaskRunnerResult taskRunnerResult = new TaskRunnerResult();
        TaskDTO task = taskMapper.queryById(instance.getTaskId());
        JSONObject formData = this.getFormData(task);

        String mainFile = formData.getString(KEY_MAIN);
        String curModelDir = folderPath + "ml_" + task.getId();
        int mainFileLen = mainFile.length();
        String codeLang = "sh ";
        String dash = " -";
        if (mainFile.substring(mainFileLen - 3, mainFileLen).equals(".py")) {
            codeLang = "python3 ";
            dash = " --";
        } else if (mainFile.substring(mainFileLen - 2, mainFileLen).equals(".r")) {
            codeLang = "R ";
        }
        String cmd = "cd " + curModelDir + "\n " + codeLang + mainFile;
        //String cmd = "cd " + curModelDir + "\n python3 " + mainFile;
        //String cmd = "cd " + curModelDir + "\n python3 " + mainFile;

        String outputDf = formData.getString(KEY_OUTPUTDF);

        for (Map.Entry entry : formData.entrySet()) {
            if ("cols".equals(entry.getKey())) {
                cmd = cmd + " --cols";
                JSONArray cols = (JSONArray) entry.getValue();
                if (cols.size() > 0) {
                    for (int i = 0; i < cols.size(); i++) {
                        if (i == 0) {
                            cmd = cmd + " " + cols.getString(i);
                        } else {
                            cmd = cmd + "," + cols.getString(i);
                        }
                    }
                }
            } else if (!"algType".equals(entry.getKey()) && !KEY_OUTPUTDF.equals(entry.getKey())
                    && !KEY_MAIN.equals((entry.getKey()))) {
                try {
                    String value = (String) entry.getValue();
                    byte[] tmpByte = value.getBytes(Charset.forName("GBK"));
                    String valueCN = (new String(tmpByte, Charset.forName("GBK")));
                    String name1 = new String(value.getBytes(), "UTF-8");
                    cmd = cmd + dash + entry.getKey() + " " + "\"" + valueCN + "\"";
                } catch (Exception e) {
                    cmd = cmd + dash + entry.getKey() + " " + "\"" + entry.getValue() + "\"";
                    logger.warn(e.toString());
                }
            }
        }

        JSONObject logInfo = new JSONObject();
        JSONObject paramsRun = new JSONObject();
        paramsRun.put("apiPath", "run");
        paramsRun.put("cmd", cmd);

        String runRes = restTemplateUtil.submitFlaskJobWebClient(paramsRun);
        JSONObject res = JSONObject.parseObject(runRes);
        if (res.containsKey("error_msg")) {
            taskRunnerResult.setStatus(500);
            logInfo.put("result", res.getString("stdout"));
            logInfo.put("error_msg", res.getString("error_msg"));
            //logInfo.put("stdout", res.getString("stdout"));
            instance.setLogInfo(logInfo.toString());
            taskInstanceMapper.update(instance);
            return taskRunnerResult;
        } else {
            logInfo.put("result", res.getString("stdout"));
        }
        logger.warn(runRes);
//        try {
//            String runRes = restTemplateUtil.submitFlaskJobWebClient(paramsRun);
//            JSONObject res = JSONObject.parseObject(runRes);
//            if (res.containsKey("error_msg")){
//                taskRunnerResult.setStatus(500);
//                logInfo.put("result", res.getString("stdout"));
//                logInfo.put("error_msg",res.getString("error_msg"));
//                //logInfo.put("stdout", res.getString("stdout"));
//                instance.setLogInfo(logInfo.toString());
//                taskInstanceMapper.update(instance);
//                return taskRunnerResult;
//            }else{
//                logInfo.put("result", res.getString("stdout"));
//            }
//            logger.warn(runRes);
//        } catch (IOException e) {
//            e.printStackTrace();
//            logger.error("error in am-run:" + e);
//        }

        String tableName = "ml_model.am_" + instance.getId();
        String save_db_script = "./common/utils/save_db.py";
        String save_db_cmd = String
                .format(saveDbTpl, save_db_script, curModelDir + "/" + outputDf, tableName,
                        instance.getId()
                        , mysqlDb, mysqlUser, mysqlPassword, mysqlHost, mysqlPort
                        , gpDbname, gpUser, gpPassword, gpHost, gpPort);
        try {
            JSONObject params = new JSONObject();
            params.put("apiPath", "recUpdate");
            params.put("cmd", save_db_cmd);
            params.put("curModelDir", curModelDir);
            String recRes = restTemplateUtil.submitFlaskJobWebClient(params);
            JSONObject recResJO = JSONObject.parseObject(recRes);
            if (recResJO.containsKey("error_msg")) {
                taskRunnerResult.setStatus(500);
                //logInfo.put("result", recResJO.getString("stdout"));
                logInfo.put("error_msg", recResJO.getString("error_msg"));
                logInfo.put("stdout", recResJO.getString("stdout"));
                instance.setLogInfo(logInfo.toString());
                taskInstanceMapper.update(instance);
                return taskRunnerResult;
            } else {
                TaskInstanceDTO instanceNew = taskInstanceMapper.queryById(instance.getId());
                instance.setDataJson(instanceNew.getDataJson());
                taskRunnerResult.setStatus(0);
                logInfo.put("recout", recResJO.getString("stdout"));
                instance.setLogInfo(logInfo.toString());
                taskInstanceMapper.update(instance);
            }
        } catch (Exception e) {
            logger.error("error in am-recUpdate:" + e);
        }
        return taskRunnerResult;
    }

    public void saveTempRecordToGP(Long taskId, Long modelId, String targetTableName) {
        String curModelDir = folderPath + "ml_" + taskId;
        String save_db_script = "./common/utils/save_file_to_gp.py";
        String save_file_cmd = String
                .format(saveFileToDBTpl, save_db_script, curModelDir + "/tmp_result.csv", targetTableName
                        , gpDbname, gpUser, gpPassword, gpHost, gpPort);
        logger.info("saveTempRecordToGP -> {}", save_file_cmd);
        try {
            JSONObject params = new JSONObject();
            params.put("apiPath", "recUpdate");
            params.put("cmd", save_file_cmd);
            params.put("curModelDir", curModelDir);
            String recRes = restTemplateUtil.submitFlaskJobWebClient(params);
            JSONObject recResJO = JSONObject.parseObject(recRes);
            logger.info("saveTempRecordToGP -> {}", recResJO.toJSONString());
        } catch (Exception e) {
            logger.error("error in am-recUpdate:" + e);
        }
    }

    public TaskRunnerResult execRunnerOld(TaskInstanceDTO instance) {
        TaskRunnerResult taskRunnerResult = new TaskRunnerResult();
        TaskDTO task = taskMapper.queryById(instance.getTaskId());
        JSONObject formData = this.getFormData(task);

        String mainFile = formData.getString(KEY_MAIN);
        String curModelDir = folderPath + "ml_" + task.getId();
        int mainFileLen = mainFile.length();
        String codeLang = "sh";
        String dash = " -";
        if (mainFile.substring(mainFileLen - 3, mainFileLen).equals(".py")) {
            codeLang = "python3";
            dash = " --";
        } else if (mainFile.substring(mainFileLen - 2, mainFileLen).equals(".r")) {
            codeLang = "R";
        }
        String cmd = "cd " + curModelDir + "\n " + codeLang + mainFile;
        //String cmd = "cd " + curModelDir + "\n python3 " + mainFile;

        String outputDf = formData.getString(KEY_OUTPUTDF);

        for (Map.Entry entry : formData.entrySet()) {
            if ("cols".equals(entry.getKey())) {
                cmd = cmd + " --cols";
                JSONArray cols = (JSONArray) entry.getValue();
                if (cols.size() > 0) {
                    for (int i = 0; i < cols.size(); i++) {
                        if (i == 0) {
                            cmd = cmd + " " + cols.getString(i);
                        } else {
                            cmd = cmd + "," + cols.getString(i);
                        }
                    }
                }
            } else if (!"algType".equals(entry.getKey()) && !KEY_OUTPUTDF.equals(entry.getKey())
                    && !KEY_MAIN.equals((entry.getKey()))) {
                cmd = cmd + dash + entry.getKey() + " " + entry.getValue();
            }
        }
        this.exeCmd(cmd);

        boolean status = false;
        File resFile = new File(curModelDir + "/" + outputDf);
        if (resFile.exists()) {
            status = true;
            String tableName = "ml_model.am_" + instance.getId();
            //todo save to gp, update instance & task
            String save_db_script = folderPath + "script/save_db.py";
            if (!new File(save_db_script).exists()) {
                restTemplateUtil.recStreaming(0L);
                try {
                    fileService.unzip(folderPath + "script.zip", new File(folderPath));
                } catch (IOException e) {
                    e.printStackTrace();
                    status = false;
                }
            }
            String save_db_cmd = String
                    .format(saveDbTpl, save_db_script, curModelDir + "/" + outputDf, tableName,
                            instance.getId()
                            , mysqlDb, mysqlUser, mysqlPassword, mysqlHost, mysqlPort
                            , gpDbname, gpUser, gpPassword, gpHost, gpPort);
            try {
                String res = this.exeCmd(save_db_cmd);
                logger.info("exeRes:" + res);
            } catch (Exception e) {
                logger.error(e.toString());
            } finally {
                TaskInstanceDTO instanceNew = taskInstanceMapper.queryById(instance.getId());
                JSONObject logInfo = new JSONObject();
                if (status) {
                    instance.setDataJson(instanceNew.getDataJson());
                    taskRunnerResult.setStatus(0);
                    logInfo.put("result", "SUCCESS");
                    instance.setLogInfo(logInfo.toString());
                } else {
                    taskRunnerResult.setStatus(500);
                    logInfo.put("result", "res");
                    instance.setLogInfo(logInfo.toString());
                }
                resFile.delete();
            }
        }

        return taskRunnerResult;
    }

    public String exeCmd(String cmd) {
        Runtime runtime = Runtime.getRuntime();
        Process process = null;
        String msg = "";
        String[] commandNArgs = new String[]{"/bin/sh", "-c", cmd};
        try {
            process = runtime.exec(commandNArgs);
        } catch (IOException e) {
            e.printStackTrace();
            return e.toString();
        }
        if (null != process) {
            InputStream inputStream = process.getInputStream();
            BufferedReader br = null;
            try {
                br = new BufferedReader(new InputStreamReader(inputStream, "gb2312"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return e.toString();
            }
            String line = null;
            while (true) {
                try {
                    if (!((line = br.readLine()) != null)) {
                        break;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    return e.toString();
                }
                System.out.println(line);
                msg = msg + line + "\n";
            }
        }
        return msg;
    }

    public void loadData(String gpDataName, Long childTaskId, Long parentTaskId) {
        String currentPath = null;
        try {
            currentPath = new java.io.File(".").getCanonicalPath();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //old version
        //String targetFilePath = currentPath + folderPath.substring(1) + "ml_" + childTaskId + "/df_" + parentTaskId + ".csv";
        String targetFilePath = folderPath + "ml_" + childTaskId + "/df_" + parentTaskId + ".csv";
        //String zipFilePath = restTemplateUtil.recStreaming(0L);
        String cmd = String
                .format(loadDataTpl, gpPassword, gpHost, gpUser, gpDbname, gpPort, gpDataName,
                        targetFilePath);
        JSONObject params = new JSONObject();
        params.put("cmd", cmd);
        params.put("apiPath", "loadData");
        try {
            restTemplateUtil.submitFlaskJob(params);
        } catch (IOException e) {
            e.printStackTrace();
            logger.error("error in am-load data:" + e);
        }
//        this.exeCmd(cmd);
    }

    public void setHeaderForResponse(HttpServletResponse response, String name) {
        name = name + ".zip";
        try {
            name = URLEncoder.encode(name, "UTF-8");
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        byte[] tmpByte = name.getBytes(Charset.forName("GBK"));
        String name2 = (new String(tmpByte, Charset.forName("GBK")));
        response.setHeader("Content-disposition",
                "attachment;filename=" + name +
                        ";filename*=utf-8''" + name);
    }

    public ApiResult<Void> moDownload(HttpServletResponse response, Long moId) throws Exception {
        String zipPath = restTemplateUtil.recStreaming(moId);
        JSONObject params = new JSONObject();
        params.put("userId", JwtUtil.getCurrentUserId());
        params.put("modelId", moId);
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("id", moId);
        JSONObject ret = this.queryByIdOnline(jsonObj);
        String filePath = folderPath + "mo_" + moId;
        File modelFile = new File(filePath);
        String modelName = ret.getString("name");
        // modelLocalMapper.queryByModelId(params).getName();
        try {
            //下载zip文件
            this.setHeaderForResponse(response, modelName);

            ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream());
            fileService.unzip(zipPath, new File(folderPath));
            modelFile = new File(filePath);
            for (String fileName : modelFile.list()) {
                FileSystemResource resource = new FileSystemResource(
                        modelFile.getAbsolutePath() + "/" + fileName);
                ZipEntry zipEntry = new ZipEntry(resource.getFilename());
                zipEntry.setSize(resource.contentLength());
                zipOut.putNextEntry(zipEntry);
                StreamUtils.copy(resource.getInputStream(), zipOut);
                zipOut.closeEntry();
            }
            zipOut.finish();
            zipOut.close();

//            deleteDirectory(new File(filePath));
        } catch (Exception e) {
            logger.error(String.valueOf(e));
            return ApiResult.valueOf(ApiResultCode.DOWNLOAD);
        } finally {
            //删除服务器zip文件
            File zipFile = new File(zipPath);
            zipFile.delete();
            modelFile.delete();
        }
        return ApiResult.valueOf(ApiResultCode.SUCCESS);
    }

    public Long copyRecord(Long oldModelLocalId, Long newProjectId){
        ModelLocalDTO modelLocalDTO = queryById(oldModelLocalId);
        modelLocalDTO.setProjectId(newProjectId);
        modelLocalDTO.setUserId(JwtUtil.getCurrentUserId());
        save(modelLocalDTO);
        return modelLocalDTO.getId();
    }


    public Long getNewRecord(Long oldModelLocalId, Long newProjectId){
        ModelLocalDTO modelLocalDTO = queryById(oldModelLocalId);
        modelLocalDTO.setProjectId(newProjectId);
        modelLocalDTO.setUserId(JwtUtil.getCurrentUserId());
        return modelLocalMapper.queryByModelIdAndProjectIdAndV(modelLocalDTO).getId();
    }
}
